From的功能实际有三个,Form验证,From添加标签,Form保留返回值
保留返回值的功能还可用ajax发送
对用户的请求的数据进行验证
使用类
从前端获取的数据的name属性的名字要和定义的class类中的字段一致
二、From添加标签
1 自己在前端写标签
自己在前端写input标签,在提交后在后台获取name属性的值进行验证,user要和LoginForm 中的字段名一致,
之后obj.is_valid()会自动验证,验证通过(通过后正确信息是obj.cleaned_data,
),返回True,不通过返回false(错误信息obj.errors),通常在前端获取,如obj.errors.user.0
这里的问题是提交后不能保存信息
1 2 3 4 5 6 7 8 9 10 11
| <form id="f1" method="POST" action="/login/"> {% csrf_token %} <p> 用户名:<input type="text" name="user">{{ obj.errors.user.0}} </p> <p> 密码:<input type="password" name="pwd">{{ obj.errors.pwd.0 }} </p> <input type="submit" value="提交"> <a href="" onclick="submitForm();">ajax提交</a> </form>
|
在前端生成的关键是在提交GET请求的时候传递obj到前端,这个obj是LoginForm实例化的对象,内部不加参数
1 2 3 4 5 6 7 8 9 10 11
| def login(request): if request.method == "GET": obj = LoginForm() return render(request, 'login.html', {'obj': obj}) return render(request, 'login.html') else: obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) return redirect('http://www.baidu.com') return render(request, 'login.html', {'obj': obj})
|
前端代码
第一个obj.user是Form生成的标签,第二个是post请求显示的错误信息
1 2 3 4 5 6 7 8 9 10 11
| <form id="f1" method="POST" action="/login/"> {% csrf_token %} <p> 用户名:{{ obj.user }}{{ obj.errors.user.0}} </p> <p> 密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }} </p> <input type="submit" value="提交"> <a href="" onclick="submitForm();">ajax提交</a> </form>
|
通过from生成标签能够保留表单已经填的通过验证的信息,不能验证通过的不保留
密码的位置加上了widget=widgets.PasswordInput(),输入的密码就是隐藏的
1 定义Form类,继承Form
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from django.shortcuts import render, redirect, HttpResponse from django.forms import Form from django.forms import fields from django.forms import widgets import json class LoginForm(Form): user = fields.CharField( max_length=18, min_length=6, required=True, error_messages={ 'max_length': '用户名太长了', 'min_length': '用户名太短了', 'required': '用户名不能为空', }, ) pwd = fields.CharField( min_length=16, required=True, error_messages={ 'min_length': '密码太短了', 'required': '密码不能为空', }, widget=widgets.PasswordInput(), )
|
2 GET请求和POST请求都发送obj
GET请求的时候需要实例化一个对象obj = LoginForm()
1 2 3 4 5 6 7 8 9 10
| def login(request): if request.method == "GET": obj = LoginForm() return render(request, 'login.html', {'obj': obj}) else: obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) return redirect('http://www.baidu.com') return render(request, 'login.html', {'obj': obj})
|
3 前端
现在的浏览器默认会在前端进行验证,在测试的时候加上novalidate,之后删除
1 2 3 4 5 6 7 8 9 10
| <form id="f1" method="POST" action="/login/" novalidate> {% csrf_token %} <p> 用户名:{{ obj.user }}{{ obj.errors.user.0}} </p> <p> 密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }} </p> <input type="submit" value="提交"> </form>
|
ajax保留返回值
1 后台代码
关键点:
- 设置字典,没有通过验证把status设置成false并把obj.errors通过json发送到后台
1 2 3 4 5 6 7 8 9 10 11 12
| def ajax_login(request): ret = {'status': True, 'msg': None} obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: ret['status'] = False ret['msg'] = obj.errors v = json.dumps(ret) return HttpResponse(v)
|
2 前端代码
- 在绑定事件的a标签上不要加href,自己点击的时候会默认刷新
- 点击的时候清除上一次的错误信息
- ajax的data发送的的时候会把字典打包成字符串。通过ajax的serialize
打包获取数据
- 错误信息的内容是通过字符串拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <form id="f1" method="POST" action="/login/" novalidate> {% csrf_token %} <p> 用户名:{{ obj.user }}{{ obj.errors.user.0}} </p> <p> 密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }} </p> <input type="submit" value="提交"> <a onclick="submitForm();">ajax提交</a> </form> <script src="/static/jquery-3.2.1.js"></script> <script> function submitForm() { $('.c1').remove(); $.ajax({ url: '/ajax_login/', type: 'POST', data: $('#f1').serialize(), dataType: 'JSON', success: function (arg) { console.log(arg); if (arg.status) { } else { $.each(arg.msg, function (index, value) { console.log(index, value); var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find('input[name="' + index + '"]').after(tag); }) } } }) } </script>
|
1 班级管理
1.1 班级form验证
班级的名字以班级开头 后面加数字
1 2 3 4 5 6 7 8 9 10 11 12 13
| from django.shortcuts import render, redirect, HttpResponse from django.forms import Form from django.forms import fields from django.forms import widgets import json from app01 import models class ClassForm(Form): """ 添加班级的验证规则 以班级开头 """ title = fields.RegexField('班级\d+')
|
1.2 班级列表
从数据库获取班级的全部信息,并发送到前端页面
1 2 3
| def class_list(request): cls_list = models.Classes.objects.all() return render(request, 'class_list.html', {'cls_list': cls_list})
|
班级列表前端是直接循环列出班级列表,row.title
1 2 3 4 5 6 7 8 9 10
| <h1>班级列表</h1> <div> <a href="/add_class/">添加班级</a> </div> <ul> {% for row in cls_list %} <li>{{ row.title }}</li> {% endfor %} </ul>
|
1.3 添加班级列表
- obj = ClassForm() # 创建标签
- 通过验证后 obj.cleaned_data 是字典类型
- 开始定义和数据库中的字段名字一致,直接在下面用**对字典的数据进行接收
- 没有通过验证返回错误的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def add_class(request): if request.method == "GET": obj = ClassForm() return render(request,'add_class.html',{'obj':obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): """ 通过验证后 obj.cleaned_data 是字典类型 在数据库添加一条数据 models.Classes.objects.create(title=obj.cleaned_data['xxx']) 如果字段的名字是xxx 就需要自己去取出来 所以开始定义和数据库中的字段名字一致,直接在下面用**对字典的数据进行接收 """ print(obj.cleaned_data) models.Classes.objects.create(**obj.cleaned_data) return redirect('/class_list/') return render(request,'add_class.html',{'obj':obj})
|
前端:
- obj.title 创建标签
- obj.errors.title.0 显示错误信息
1 2 3 4 5 6 7
| <h1>添加班级</h1> <form method="POST" action="/add_class/"> {% csrf_token %} {{ obj.title }} {{ obj.errors.title.0 }} {# {{ obj }}#} <input type="submit" value="提交"> </form>
|
1.4 编辑班级
- url 中的正则加上(\d+)
- 页面的nid 是通过在class_list上点击的时候获取的,把row.id传递
<a class="btn btn-primary" href="/edit_class/">编辑</a>
- 在后台函数中用参数nid进行接收
- 使用initial进行参数传递
- 编辑是用update对数据库进行更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def edit_class(request, nid): """ nid 负责接收 class页面中的row.id中的值 在URL中设置edit_class/(\d+)/ :param request: :param nid: :return: """ if request.method == "GET": row = models.Classes.objects.filter(id=nid).first() obj = ClassForm(initial={'title': row.title}) return render(request, 'edit_class.html', {'nid': nid, 'obj': obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): models.Classes.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/class_list/') return render(request, 'edit_class.html', {'obj': obj})
|
2 学生管理
2.1学生管理的后端
- 单选的时候用的而是插件Select中的choice
- choice中的参数是列表,列表中的元素是元组
- value_list操作中获得是元组对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class StudentForm(Form): name = fields.CharField( min_length=2, max_length=6, ) email = fields.EmailField() age = fields.IntegerField(min_value=18, max_value=100) cls_id = fields.IntegerField( widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title')) ) def student_list(request): stu_list = models.Students.objects.all() return render(request, 'student_list.html', {'stu_list': stu_list}) def add_student(request): if request.method == "GET": obj = StudentForm() return render(request, 'add_student.html', {'obj': obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Students.objects.create(**obj.cleaned_data) return redirect('/student_list/') return render(request, 'add_student.html', {'obj': obj})
|
BUG修复 动态更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| cls_id = fields.IntegerField( widget=widgets.Select( attrs={'class': 'form-control'}) ) def __init__(self, *args, **kwargs): super(StudentForm, self).__init__(*args, **kwargs) self.fields['cls_id'].widget.choices = models.Classes.objects.values_list('id', 'title')
|
2.2 前端处理
- 有外键的可以直接点出来
row.cls.title
学生列表
1 2 3 4 5 6 7 8 9 10
| <h1>学生列表</h1> <a href="/add_student/">添加</a> <ul> {% for row in stu_list %} <li> {{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }} <a href="/edit_student/{{ row.id }}">编辑</a> </li> {% endfor %} </ul>
|
添加学生,标签通过后台生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <h1>添加学生</h1> <form action="/add_student/" method="POST"> {% csrf_token %} <p> 姓名:{{ obj.name }} {{ obj.errors.name.0 }} </p> <p> 邮箱: {{ obj.email }} {{ obj.errors.email.0 }} </p> <p> 年龄: {{ obj.age }} {{ obj.errors.age.0 }} </p> <p> 班级 {{ obj.cls_id }} {{ obj.errors.cls_id.0 }} </p> <input type="submit" value="提交"> </form>
|
编辑学生
- 通过student_list 中获取了row.id
- row.id 传递到后台
- initial接收的是字典对象
- 正好values获取的是字典对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def edit_student(request,nid): """ 获取编辑学生的的信息并展现在输入框中 使用initial :param request: :param nid: :return: """ if request.method == "GET": row = models.Students.objects.filter(id=nid).values( 'name','email','age','cls_id' ).first() obj = StudentForm(initial=row) return render(request,'edit_student.html',{'nid':nid,'obj':obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Students.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/student_list/') return render(request,'edit_student.html',{'obj':obj})
|
前端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> </head> <body> <div style="width: 500px; margin: 80px auto;"> <form method="POST" action="/edit_student/{{ nid }}/"> {% csrf_token %} <div class="form-group"> <label for="exampleInputEmail1">姓名</label> {{ obj.name }} </div> <div class="form-group"> <label for="exampleInputPassword1">邮箱</label> {{ obj.email }} </div> <div class="form-group"> <label for="exampleInputPassword1">年龄</label> {{ obj.age }} </div> <div class="form-group"> <label for="exampleInputPassword1">班级</label> {{ obj.cls_id }} </div> <input type="submit" value="提交"> </form> </div> </body> </html>
|
3 教师管理
form的执行过程:
1.找到所有的的字段 tname xx(这些都是静态字段)
2.把字段放到self.fields中{}是字典的形式
3.程序从上到下执行,启动后从数据库获取一次数据,然后就不变了,导致数据不能动态刷新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| def teacher_list(request): tea_list = models.Teacher.objects.all() return render(request, 'teacher_list.html', {'tea_list': tea_list}) class TeacherForm(Form): """ form的执行过程: 1.找到所有的的字段 tname xx(这些都是静态字段) 2.把字段放到self.fields中{}是字典的形式 3.程序从上到下执行,启动后从数据库获取一次数据,然后就不变了,导致数据不能动态刷新 """ tname = fields.CharField(min_length=2) xx = fields.MultipleChoiceField( widget=widgets.SelectMultiple ) """ 自己重写的方法 1.从self.fields的字典中找xx,然后找xx的choice,choice=[(),()]的形式 2.源码中会循环所有的字段,把有Field的加入self。Fields中,因为都是继承Fields """ def __init__(self, *args, **kwargs): super(TeacherForm, self).__init__(*args, **kwargs) self.fields['xx'].choices = models.Classes.objects.values_list('id', 'title')
|
添加老师
1 2 3 4 5 6 7 8 9 10 11 12
| def add_teacher(request): if request.method == "GET": obj = TeacherForm() return render(request, 'add_teacher.html', {'obj': obj}) else: obj = TeacherForm(request.POST) if obj.is_valid(): xx = obj.cleaned_data.pop('xx') row = models.Teacher.objects.create(**obj.cleaned_data) row.c2t.add(*xx) return redirect('/teacher_list/') return render(request, 'add_teacher.html', {'obj': obj})
|
编辑老师
打开的时候有默认值, obj = TeacherForm(initial={‘tname’: row.tname, ‘xx’: id_list}) # 显示的默认值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def edit_teacher(request, nid): if request.method == "GET": row = models.Teacher.objects.filter(id=nid).first() class_ids = row.c2t.values_list('id') print(class_ids) id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else [] obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list}) return render(request, 'edit_teacher.html', {'obj': obj})
|
生成标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>编辑老师</h1> <form action=""> 教师姓名 <p>{{ obj.tname }}</p> 任教班级<p> {{ obj.xx }}</p> </form> </body> </html>
|
总结
Select框:
单选:单选是用CharField IntegerField 和Select搭配
ChoiceField也是用于单选框,但是choice写在外面
cls_id = fields.IntegerField(
# widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])
widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})
)
cls_id = fields.ChoiceField(
choices=models.Classes.objects.values_list('id','title'),
widget=widgets.Select(attrs={'class': 'form-control'})
)
obj = FooForm({'cls_id':1})
多选:
多选只能用MultipleChoiceField ,且choices放在外面。最后重写init方法后就不用了
xx = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id','title'),
widget=widgets.SelectMultiple
)
obj = FooForm({'cls_id':[1,2,3]})
通过Bootstrap进行界面美化
bootstrap 的关键是给相应的标签添加class属性
如:添加form-control
1 2 3
| <p> 测试:<input type="text" class="form-control"> {# 使用bootstrap #} </p>
|
对于自己生成的标签,是通过Form组件的插件添加属性的 widgets
attrs后面对应的是字典类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class StudentForm(Form): name = fields.CharField( min_length=2, max_length=6, widget = widgets.TextInput(attrs={'class':'form-control'}) # 给输入框添加属性 ) email = fields.EmailField( widget=widgets.TextInput(attrs={'class': 'form-control'}) # 给输入框添加属性 ) age = fields.IntegerField( min_value=18, max_value=100, widget = widgets.TextInput(attrs={'class': 'form-control'}) # 给输入框添加属性 ) cls_id = fields.IntegerField( # widget = widgets.Select(choices=[(1,'上海'),(1,'北京')]) # choices = 列表的内部是元组的形式 widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'),attrs={'class': 'form-control'}) # choice 是应用于单选 value_list 获取的就是列表中有元组 # 注意此处有bug 添加班级后在这里没有新添加的班级,下面进行修复 )
|